#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
const $ = new Env('香蕉视频py')
cron: 10 1 * * *
香蕉视频 APP
扫码下载：https://pic.rmb.bdstatic.com/bjh/240914/dc8411e749492d270032b5278c5abd321993.png
每日签到及任务获得金币, 金币可提现或在棋牌处单车变摩托, 10 金币 = 1元, 保底每日 6 金币
手机号#密码
export BANANA_ACCOUNT = '18888888888#123456'
多账号用 & 或换行
@author Telegram@sudojia
@site https://blog.imzjw.cn
@date 2024/09/14
源：https://github.com/sudojia/AutoTaskScript/blob/script/src/client/sudojia_bananaVideo.js
原来是程序是js写的，这是AI替换的py版本，程序逻辑基本相同，只是语法不同。
"""

import os, time, random, requests, functools

# ---------- 聚合推送配置 ----------
AGGRE_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'dailylog')

IMMEDIATE = False          # True 实时推送，False 汇总推送
def push(title, text=""):
    if IMMEDIATE:
        from notify import send
        send(title, text)
    else:
        with open(AGGRE_FILE, "a", encoding="utf-8") as f:
            f.write(f"{title}\n{text}\n{'-'*40}\n")

from datetime import datetime, timedelta

# 配置参数
MAX_RETRIES = 10  # 最大重试次数
RETRY_DELAY = 5  # 重试延迟时间(秒)
OPERATION_TIMEOUT = 3600  # 单个操作超时时间(秒)
TOTAL_TIMEOUT = 3600  # 总程序超时时间(秒)

BASE_URL = "https://mgcrjh.ipajx0.cc"
# BASE_URL = "https://h5.xxoo221.org"
UA_POOL = [
    "Mozilla/5.0 (Linux; Android 11; M2007J3SC) AppleWebKit/537.36 Chrome/96.0 Mobile Safari/537.36",
    "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605 Version/15.0 Mobile/15E148 Safari/604.1",
]
HEADERS = {
    "x-system": "Android", "x-channel": "xj1", "x-version": "5.0.5",
    "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9",
}

# 超时异常类
class TimeoutException(Exception):
    pass

# 重试装饰器
def retry(times=MAX_RETRIES, delay=RETRY_DELAY):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            last_exception = None
            for attempt in range(times):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    last_exception = e
                    if attempt < times - 1:
                        log(f"操作失败，将在{delay}秒后重试 (尝试 {attempt+1}/{times})：{str(e)}", args[-1] if args else None)
                        time.sleep(delay)
            log(f"达到最大重试次数 {times} 次，操作失败", args[-1] if args else None)
            if last_exception:
                raise last_exception
            else:
                raise Exception("操作失败，但未捕获到具体异常")
        return wrapper
    return decorator

# 超时装饰器
def timeout(seconds=OPERATION_TIMEOUT):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start_time = datetime.now()
            
            def check_timeout():
                if (datetime.now() - start_time) > timedelta(seconds=seconds):
                    raise TimeoutException(f"操作超时 ({seconds}秒)")
            
            # 保存原始的log函数参数，用于超时日志
            msgs = args[-1] if args and isinstance(args[-1], list) else None
            
            try:
                # 执行函数前先检查总超时
                if 'total_start_time' in kwargs:
                    total_elapsed = (datetime.now() - kwargs['total_start_time']).total_seconds()
                    if total_elapsed > TOTAL_TIMEOUT:
                        raise TimeoutException(f"总程序超时 ({TOTAL_TIMEOUT}秒)")
                
                result = func(*args, **kwargs)
                check_timeout()
                return result
            except TimeoutException:
                raise
            except Exception as e:
                check_timeout()
                raise e
        return wrapper
    return decorator

def rnd_ua():
    return random.choice(UA_POOL)

def wait(a, b):
    time.sleep(random.randint(a, b) / 1000)

import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)   # 关掉烦人的警告

def sess():
    s = requests.Session()
    s.headers.update({**HEADERS, "User-Agent": rnd_ua()})

    # 1. 关闭 HTTPS 证书校验
    s.verify = False

    # 2. 给所有请求统一加超时
    orig = s.request
    s.request = lambda *a, **kw: orig(*a, timeout=10, **kw)
    return s

@retry()
@timeout()
def login(s, phone, pwd, msgs=None):
    data = {"logintype": 0, "mobiprefix": 86, "mobi": phone, "password": pwd}
    return s.post(f"{BASE_URL}/login", data=data).json()

@retry()
@timeout()
def get_user_info(s, msgs=None):
    return s.get(f"{BASE_URL}/ucp/index").json()

@retry()
@timeout()
def sign(s, msgs=None):
    return s.post(f"{BASE_URL}/ucp/task/sign", data={}).json()

@retry()
@timeout()
def add_favorite(s, vodid, msgs=None):
    return s.post(f"{BASE_URL}/favorite/add", data={"vodid": vodid}).json()

@retry()
@timeout()
def remove_favorite(s, vodids, msgs=None):
    return s.post(f"{BASE_URL}/favorite/remove", data={"vodids": vodids}).json()

@retry()
@timeout()
def ad_click(s, msgs=None):
    return s.get(f"{BASE_URL}/ucp/task/adviewClick").json()

@retry()
@timeout()
def download_video_task(s, vodid, msgs=None):
    return s.get(f"{BASE_URL}/vod/reqdown/{vodid}").json()

@timeout(3600)  # 观看视频超时时间更长
def watch_video(s, total_start_time, msgs=None):
    cg = 0
    fav_list = []
    while cg < 10:
        try:
            vodid = random.randint(1, 76000)
            url = f"{BASE_URL}/v2/vod/reqplay/{vodid}"
            
            # log(f"观影号{vodid}",msgs)
            r = s.get(url).json()
            if r.get("retcode") == 0:
                cg += 1
                r = add_favorite(s, vodid, msgs)
                if r.get("retcode") == 0:
                    fav_list.append(vodid)                                                   
                    print(f"{vodid}观看影片：{cg}/10")
            else:
                print(f"{vodid}观看影片 {cg}/10 失败: {r.get('errmsg')}")
        except Exception as e:
            log(f"观看影片 {cg}/10 出错: {str(e)}", msgs)
            
        wait(1500, 2300)
    if fav_list:
        remove_favorite(s, ",".join(map(str, fav_list)), msgs)
        log("已完成观看和收藏任务，金币+2", msgs)
@retry()
@timeout()
def save_qrcode(s, msgs=None):
    return s.get(f"{BASE_URL}/ucp/task/qrcodeSave").json()

@retry()
@timeout()
def post_comment(s, vodid, msgs=None):
    return s.post(f"{BASE_URL}/comment/post", data={"vodid": vodid, "content": "好"}).json()

def log(msg, msgs):
    if msg:
        print(msg)
        if msgs is not None:
            msgs.append(msg)

def run_one(idx, phone, pwd, msgs, total_start_time):
    log(f"\n===== 账号[{idx}] {phone} =====", msgs)
    s = sess()

    try:
        login_json = login(s, phone, pwd, msgs)
        if login_json.get("retcode") != 0:
            log(f"登录失败：{login_json.get('errmsg')}", msgs)
            return
        s.headers["X-Cookie-Auth"] = login_json["data"]["xxx_api_auth"]
        log("登录成功", msgs)

        info = get_user_info(s, msgs)
        if info.get("retcode") != 0:
            log(f"获取用户信息失败：{info.get('errmsg')}", msgs)
            return
        username = info["data"]["user"]["username"]
        log(f"昵称：{username}", msgs)

        # 签到任务
        sign_json = sign(s, msgs)
        if sign_json.get("retcode") != 0:
            log(f"签到失败：{sign_json.get('errmsg')}", msgs)
            sign_msg = "签到失败"
        else:
            coin = sign_json["data"]["taskdone"]
            log(f"签到成功，金币 +{coin}", msgs)
            sign_msg = "签到成功"
        
        # 广告任务
        ad_r = ad_click(s, msgs)
        if ad_r.get("retcode") == 0:
            log(f"广告点击成功，金币 +{ad_r['data']['taskdone']}", msgs)

        # 下载任务
        down_r = download_video_task(s, random.randint(1, 66000), msgs)
        if down_r.get("retcode") == 0 and down_r["data"].get("taskdone"):
            log(f"下载长视频任务成功，金币 +{down_r['data']['taskdone']}", msgs)

        # 观看视频且收藏任务
        watch_video(s, total_start_time, msgs)

        # 保存二维码任务
        qr_r = save_qrcode(s, msgs)
        if qr_r.get("retcode") == 0:
            log(f"保存二维码成功，金币 +{qr_r['data']['taskdone']}", msgs)
        
        # 评论任务
        cmt_r = post_comment(s, random.randint(1, 66000), msgs)
        if cmt_r.get("retcode") == 0:
            log("发布评论成功", msgs)
        
        # 获取当前金币
        gold = get_user_info(s, msgs)["data"]["user"]["goldcoin"]
        log(f"当前金币：{gold}", msgs)

    except TimeoutException as e:
        log(f"超时错误: {str(e)}", msgs)
        msgs.append(f"账号[{idx}] {phone} 超时: {str(e)}")
    except Exception as e:
        log(f"操作错误: {str(e)}", msgs)
        msgs.append(f"账号[{idx}] {phone} 错误: {str(e)}")

def main():
    total_start_time = datetime.now()
    raw = os.environ.get("BANANA_ACCOUNT", "").strip()
    accounts = [a.strip() for a in raw.split("&") if a.strip()]
    if not accounts:
        print("未配置 BANANA_ACCOUNT"); return

    msgs = []
    try:
        for idx, acc in enumerate(accounts, 1):
            # 检查总超时
            elapsed = (datetime.now() - total_start_time).total_seconds()
            if elapsed > TOTAL_TIMEOUT:
                raise TimeoutException(f"总程序超时 ({TOTAL_TIMEOUT}秒)")
                
            try:
                phone, pwd = acc.split("#", 1)
                run_one(idx, phone, pwd, msgs, total_start_time)
            except Exception as e:
                msgs.append(f"账号[{idx}] 异常：{e}")
            wait(2000, 2500)

        push("香蕉视频", "\n".join(msgs))
        
    except TimeoutException as e:
        log(f"程序终止: {str(e)}", msgs)
        push("香蕉视频", f"程序终止: {str(e)}\n\n{''.join(msgs)}")

if __name__ == "__main__":
    # 测试用环境变量
    # os.environ['BANANA_ACCOUNT'] = ''
    main()
